﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Diagnostics;

namespace Master
{
    public class InterSection : Label
    {
        /*************
         * VARIABLES *
         *************/
        private iTrafficLightStrategy currentStrategy;
        private Dictionary<Side, List<Road>> roadDictionary = new Dictionary<Side, List<Road>>();
        private TrafficMarshal trafficMarshal;

        /********************
         * PUBLIC FUNCTIONS *
         ********************/
        public TrafficMarshal getTrafficMarshal()
        {
            return trafficMarshal;
        }

        public void stopStrategy(object sender, EventArgs e)
        {
            currentStrategy.stop();
        }

        public void init(object sender, EventArgs e)
        {
            setupRoadDictionary();
            currentStrategy = TrafficLightStrategyFactory.createThreeLaneStrategy();
            if (Background == Brushes.Transparent)
                this.Background = Brushes.Gray;
            currentStrategy.start(roadDictionary);
            trafficMarshal = null;
        }

        public List<Road> getRoadsBySide(Side side)
        {
            return roadDictionary[side];
        }

        public Lane getExitLane(DirectionData directionData, Point carCenterPoint)
        {
            int exitOrientation = directionData.getOrientation();
            if (directionData.getDirection() == Direction.FORWARD)
                return getFowardExitLane(exitOrientation, carCenterPoint);
            if (directionData.getDirection() == Direction.LEFT)
                exitOrientation = Toolkit.alterOrientationBy(exitOrientation, -90);
            else if (directionData.getDirection() == Direction.RIGHT)
                exitOrientation = Toolkit.alterOrientationBy(exitOrientation, 90);
            return getExitLaneFromDirectionData(exitOrientation, directionData);
        }

        /*********************
         * PRIVATE FUNCTIONS *
         *********************/
        private void setupRoadDictionary()
        {
            //Get x and y
            double x = Canvas.GetLeft(this);
            double y = Canvas.GetTop(this);

            //getTopSide
            Point pointer = new Point(x + 1, y - 10);
            addRoad(Side.TOP, pointer);

            //getRightSide
            pointer = new Point(x + Width + 10, y + 1);
            addRoad(Side.RIGHT, pointer);

            //getBottomSide
            pointer = new Point(x + 1, y + Height + 10);
            addRoad(Side.BOTTOM, pointer);

            //getLeftSide
            pointer = new Point(x - 10, y + 1);
            addRoad(Side.LEFT, pointer);
        }

        private void addRoad(Side side, Point pointer)
        {
            List<Road> roadList = getRoadsBySide(side, pointer);
            if (roadList.Count > 0)
                roadDictionary.Add(side, getRoadsBySide(side, pointer));
        }

        private List<Road> getRoadsBySide(Side side, Point pointer)
        {
            List<Road> roadList = new List<Road>();
            double startX = pointer.X;
            double startY = pointer.Y;
            Road road;
            switch (side)
            {
                case Side.BOTTOM:
                case Side.TOP:
                    while ((road = findRoad(roadList, pointer)) != null)
                    {
                        roadList.Add(road);
                        pointer.X += road.ActualWidth;
                    }
                    break;
                case Side.LEFT:
                case Side.RIGHT:
                    while ((road = findRoad(roadList, pointer)) != null)
                    {
                        roadList.Add(road);
                        pointer.Y += road.ActualHeight;
                    }
                    break;
            }
            roadList = setupTrafficLights(roadList, side);
            return roadList;
        }

        private Road findRoad(List<Road> roadList, Point pointer)
        {
            Road road = Map.INSTANCE.getRoadAtPoint(pointer);
            if (road != null && !roadList.Contains(road))
                return road;
            return null;
        }

        private List<Road> setupTrafficLights(List<Road> roadList, Side side)
        {
            foreach (Road road in roadList) foreach (Lane lane in road.getLanes())
            {
                if ((side == Side.TOP && lane.Orientation == 180) ||
                    (side == Side.RIGHT && lane.Orientation == 270) ||
                    (side == Side.BOTTOM && lane.Orientation == 0) ||
                    (side == Side.LEFT && lane.Orientation == 90))
                {
                    lane.setTrafficLight();
                    lane.setTrafficArrow();
                    lane.Type = LaneType.DRIVEWAY;
                }
                else //no traffic light --> exit lane
                {
                    lane.Type = LaneType.EXIT;
                }
                lane.setInterSection(this);
            }
            return roadList;
        }

        private Lane getFowardExitLane(int exitOrientation, Point carCenterPoint)
        {
            Road exitRoad = getExitRoadByOrientation(exitOrientation);
            foreach (Lane lane in exitRoad.getLanes())
                if (((exitOrientation == 0 || exitOrientation == 180) && Toolkit.elementIsAtX(lane, carCenterPoint.X)) ||
                    ((exitOrientation == 90 || exitOrientation == 270) && Toolkit.elementIsAtY(lane, carCenterPoint.Y)))
                    return lane;
            return null;
        }

        private Lane getExitLaneFromDirectionData(int exitOrientation, DirectionData directionData)
        {
            Road exitRoad = getExitRoadByOrientation(exitOrientation);
            if (directionData.getDirection() == Direction.LEFT)
                return exitRoad.getLeftLane();
            else  
                return exitRoad.getRightLane();

        }

        private Road getExitRoadByOrientation(int orientation)
        {
            return getExitRoadBySide(Toolkit.orientationToSide(orientation));
        }

        private Road getExitRoadBySide(Side side)
        {
            List<Road> roadList = roadDictionary[side];
            Road road = null;
            foreach (Road nextRoad in roadList)
                if (nextRoad.getLanes()[0].Type == LaneType.EXIT)
                {
                    road = nextRoad;
                    break;
                }
            return road;
        }
    }
}
